package kiwi.core.services.security;

import kiwi.core.api.config.ConfigurationService;
import kiwi.core.api.security.OAuthTokenIssuerService;
import kiwi.core.api.security.TokenManagerService;
import kiwi.core.model.security.AccessToken;
import kiwi.core.model.security.RefreshToken;
import kiwi.core.model.user.KiWiUser;
import org.apache.amber.oauth2.as.issuer.MD5Generator;
import org.apache.amber.oauth2.as.issuer.OAuthIssuer;
import org.apache.amber.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.amber.oauth2.as.request.OAuthTokenRequest;
import org.apache.amber.oauth2.as.response.OAuthASResponse;
import org.apache.amber.oauth2.common.exception.OAuthProblemException;
import org.apache.amber.oauth2.common.exception.OAuthSystemException;
import org.apache.amber.oauth2.common.message.OAuthResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;
import java.util.Enumeration;

/**
 * OAuthTokenIssuerService
 * User: Stephanie Stroka
 * Date: 06.05.2011
 * Time: 13:17:47
 */
public class OAuthTokenIssuerServiceImpl implements OAuthTokenIssuerService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Inject
    private TokenManagerService tokenManagerService;

    @Inject
    private ConfigurationService configurationService;

    /**
     * constructOAuthResponse() constructs an OAuthResonse entity for a user, which
     * can be sent to the client. The constructed accessToken and refreshToken
     * are stored in memory together with the expiration time and the associated
     * user. 
     * @param request
     * @param user
     * @return
     */
    public OAuthResponse constructOAuthResponse(HttpServletRequest request, KiWiUser user) {
        OAuthTokenRequest oauthRequest = null;
        OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
        try {
            if(request != null) {
                Enumeration<String> parameterNames = request.getParameterNames();
            }
            oauthRequest = new OAuthTokenRequest(request);

            if(oauthRequest == null) {
                log.error("OAuthRequest received a bad HttpServletRequest.");
                 OAuthResponse r = null;
                try {
                    r = buildASErrorResponse
                            (400, OAuthProblemException.error("OAuthRequest received a bad HttpServletRequest"));
                } catch (OAuthSystemException e1) {
                    e1.printStackTrace();
                }
                return r;
            }

            String accessToken = generateAccessToken(oauthIssuerImpl);
            String refreshToken = generateRefreshToken(oauthIssuerImpl);
            Integer exp_Int = getDefaultExpiration();

            String expStr = exp_Int.toString();

            OAuthResponse r = buildASTokenResponse(accessToken, expStr, refreshToken);

            tokenManagerService.setToken(user, accessToken, refreshToken, exp_Int.intValue());

            return r;

        } catch (OAuthSystemException e) {
            OAuthResponse r = null;
            try {
                r = buildASErrorResponse(
                        401, OAuthProblemException.error(e.getMessage()));
            } catch (OAuthSystemException e1) {
                e1.printStackTrace();
            }

            log.debug(r.getBody());

            return r;
        } catch (OAuthProblemException e) {
            OAuthResponse r = null;
            try {
                r = buildASErrorResponse(401, e);
            } catch (OAuthSystemException e1) {
                e1.printStackTrace();
            }

            log.debug(r.getBody());

            return r;
        }
    }

        /**
     * refreshToken() constructs a new access token for a user
     * @param refreshToken
     * @return newAccessToken, a new access token assigned for the user holding the
     *          given refreshToken.
     */
    @Override
    public OAuthResponse refreshOAuthToken(String refreshToken) {

        OAuthResponse r = null;
        String newAccessToken = null;
        RefreshToken refreshTokenEntity;
        if((refreshTokenEntity = tokenManagerService.getRefreshToken(refreshToken)) == null) {
            log.error("Refresh token could not be found.");
            return null;
        }

        // remove old access token
        AccessToken accessTokenEntity = refreshTokenEntity.getAccessToken();
        KiWiUser user = accessTokenEntity.getKiwiUser();
        tokenManagerService.removeAccessToken(accessTokenEntity.getAccessToken());

        OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());

        try {
            newAccessToken = generateAccessToken(oauthIssuerImpl);

            int exp = getDefaultExpiration();
            tokenManagerService.setToken(user, newAccessToken, refreshToken, exp);

            r = buildASTokenResponse(newAccessToken, String.valueOf(exp), refreshToken);

        } catch (OAuthSystemException e) {
            e.printStackTrace();
            log.error("{}", e.getMessage());
        }
        return r;
    }

    private OAuthResponse buildASTokenResponse(String accessToken, String expiration, String refreshToken) throws OAuthSystemException {
        return OAuthASResponse
                .tokenResponse(HttpServletResponse.SC_OK)
                .setAccessToken(accessToken)
                .setExpiresIn(expiration)
                .setRefreshToken(refreshToken)
                .buildJSONMessage();
    }

    private OAuthResponse buildASErrorResponse(int statuscode, OAuthProblemException e) throws OAuthSystemException {
        return OAuthResponse
                .errorResponse(statuscode)
                .error(e)
                .buildJSONMessage();
    }

    private String generateAccessToken(OAuthIssuer oauthIssuer) throws OAuthSystemException {
        return oauthIssuer.accessToken();
    }

    private String generateRefreshToken(OAuthIssuer oauthIssuer) throws OAuthSystemException {
        return oauthIssuer.refreshToken();
    }

    private int getDefaultExpiration() {
        int exp;
        if(configurationService.getConfiguration("session_expiration") == null) {
            exp = 3600;
            configurationService.setConfiguration("session_expiration", String.valueOf(3600));
        } else {
            String expStr = (String) configurationService.getConfiguration("session_expiration");
            log.debug("expStr: {}", expStr);
            
            exp = Integer.parseInt(expStr);
        }
        return exp;
    }
}
